package com.yeziji.devops.config.aspect;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.datasource.FlexDataSource;
import com.yeziji.common.context.OnlineContext;
import com.yeziji.devops.annotation.DevopsUseDataSource;
import com.yeziji.devops.common.DevopsKey;
import com.yeziji.devops.utils.ParseUtils;
import com.yeziji.utils.expansion.Opt2;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.Optional;

/**
 * {@link com.yeziji.devops.annotation.DevopsUseDataSource} 切面
 *
 * @author hwy
 * @since 2024/10/10 17:04
 **/
@Slf4j
@Aspect
@Component
public class DevopsUseDataSourceAspect {
    @Around("@within(devopsUseDataSource) || @annotation(devopsUseDataSource)")
    public Object devopsUseDataSourceHandler(ProceedingJoinPoint pjp, DevopsUseDataSource devopsUseDataSource) throws Throwable {
        devopsUseDataSource = ParseUtils.getAnnotation(pjp, DevopsUseDataSource.class);
        if (devopsUseDataSource == null) {
            return pjp.proceed();
        }

        String originalKey = DataSourceKey.get();
        // 避免 npe 这里指定初始化出来的 master 数据源
        String dataSourceKey = Opt2.nullElse(DataSourceKey.get(), DevopsKey.DataSource.MASTER);
        // 默认数据源优先级最高
        boolean isDefault = devopsUseDataSource.useDefault();
        FlexDataSource dataSource = FlexGlobalConfig.getDefaultConfig().getDataSource();
        if (isDefault && dataSource != null) {
            dataSourceKey = Optional.ofNullable(OnlineContext.getDataSourceKey()).orElse(dataSource.getDefaultDataSourceKey());
        } else if (StrUtil.isNotBlank(devopsUseDataSource.value())){
            dataSourceKey = devopsUseDataSource.value();
        }

        // 如果 key 相同, 什么也不用做
        if (Objects.equals(originalKey, dataSourceKey)) {
            return pjp.proceed();
        }

        // 执行切库操作
        try {
            DataSourceKey.use(dataSourceKey);
            return pjp.proceed();
        } finally {
            // 正常没切库情况下 originalKey 是为 null，如果有切过库，说明可能是先切库，又需要再切的场景
            if (originalKey == null) {
                DataSourceKey.clear();
            } else {
                DataSourceKey.use(originalKey);
            }
        }
    }
}